package org.example.spring02aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Order(0)

@Component
@Aspect // 告诉Spring这是一个切面类
public class LogAspect {

    /*
    * 告诉spring,以下通知方法何时何地运行
    * 何时：
    *     @Before:方法执行之前运行
    *     @AfterReturning:方法正常返回运行
    *     @AfterThrowing:方法抛异常时运行
    *     @After:方法执行之后运行（无论是否正常执行）
    * 何地：切入点表达式：
    *     execution(方法的全签名)：
    *               全写法：e.g. (public) void (org.example.spring.ioc.service.)UserService.add(int,int)[throws ArithmeticException]
    *               省略写法：e.g.void add(int,int)
    *       通配符：*：任意参数
    *       ..：任意参数个数
    *       ()：方法无参数
    */

    //增强器链：切面中所有的通知方法其实就是增强器；他们被组织到一个链路放到集合中，目标方法真正执行前后，回去增强其脸中执行那些需要提前执行的方法

    /*AOP的底层原理：
    1.Spring会为每个被切面切入的组件创建代理对象（Spring CGLIB 创建的代理对象，无视接口）
    2.代理对象中保存了切面类里面所有通知方法构成的增强器链
    3.目标方法执行前，会调用增强器链中的通知方法
    * */

    //简化切入点表达式
    @Pointcut("execution(int *(int,int))")
    public void pointCut()
    {

    }

    //JoinPoint:封装了当前目标方法的所有信息

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint)
    {
        //1.拿到方法全签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //2.拿到方法名
        String name = signature.getName();

        //3.拿到参数列表
        Object[] args = joinPoint.getArgs();
        System.out.println("【切面 - 日志】【"+name+"】开始：参数列表:【"+ Arrays.toString( args)+"】");
    }

    @AfterReturning(value = "pointCut()",returning = "result")//returning="result"获取目标方法的返回值
    public void logEnd(JoinPoint joinPoint, Object result)
    {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        String name = signature.getName();
        System.out.println("【切面 - 日志】【"+name+"】返回：结果：【"+result+"】");
    }

    @AfterThrowing(value="pointCut()",throwing = "e")//throwing="e"获取目标方法抛出的异常
    public void logException(JoinPoint joinPoint, Exception e)
    {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        String name = signature.getName();

        System.out.println("【切面 - 日志】【"+name+"】异常：异常信息：【"+e.getMessage()+"】");
    }

    @After("pointCut()")
    public void logReturn(JoinPoint joinPoint)
    {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        String name = signature.getName();
        System.out.println("【切面 - 日志】【"+name+"】结束");
    }

    // 参数匹配:参数带啥就切
    @Before("args(int,int)")
    public void haha(){
        System.out.println("【切面 - 日志】哈哈哈哈哈");
    }

    //@args:匹配参数上有没有标注解

    //@annotation:匹配方法上有没有标注解
    @Before("@annotation(org.example.spring02aop.annotation.MyAnnotation)")
    public void myAnnotatedMethod() {
        System.out.println("【切面 - 日志】方法上标注了指定注解");
    }
}
